﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using log4net.Core;
using CommonLibrary.utils;
using log4net.Layout;
using System.Xml;

namespace CommonLibrary
{
    /// <summary>
    /// Uses log4net to log the information to a file. The caller's information are logged as well.
    /// The App.config file contains various options with which the logging can be customised.
    /// </summary>
    public class Logging
    {
        #region Declarations

        CommonEnums.GB_MODULES gbModuleName;
        log4net.ILog log;

        #endregion

        #region Public Members

        public Logging(CommonEnums.GB_MODULES inGBApplicationLayer, string inName)
        {
            gbModuleName = inGBApplicationLayer;

            // Creat logger for the caller class
            log = log4net.LogManager.GetLogger(inName);
        }

        public void LogInfo(string inLogMessage)
        {
            if (log.IsInfoEnabled)
            {
                Log(Level.Info, null, inLogMessage);
            }
        }

        public void LogDebug(string inLogMessage)
        {
            if (log.IsDebugEnabled)
            {
                Log(Level.Debug, null, inLogMessage);
            }
        }

        public void LogWarn(string inLogMessage)
        {
            if (log.IsWarnEnabled)
            {
                Log(Level.Warn, null, inLogMessage);
            }
        }

        public void LogError(Exception inException, string inLogMessage)
        {
            if (log.IsErrorEnabled)
            {
                Log(Level.Error, inException, inLogMessage);
            }
        }

        #endregion

        #region Private Members

        /// <summary>
        /// Gets the Caller's information using stackTrace and logs the information
        /// </summary>
        /// <param name="inlevel"></param>
        /// <param name="inException"></param>
        /// <param name="inLogMessage"></param>
        private void Log(Level inlevel, Exception inException, string inLogMessage)
        {
            try
            {
                #if DEBUG

                // Instantiate Stack variables to get the caller's information
                StackTrace stack = new StackTrace(true);
                StackFrame frame;
                MethodBase method;
                Type type;
                string className = string.Empty;
                int lineNumber = 0;

                // Get the caller class and method information
                frame = stack.GetFrame(2);
                method = frame.GetMethod();
                type = method.DeclaringType;
                className = type.FullName;
                lineNumber = frame.GetFileLineNumber();

                // Construct a message containing the caller information such as 
                // ModuleName, class, function and the lineNumber where logger was called
                inLogMessage = string.Concat("Module:" + gbModuleName +
                                             ", Class:" + className +
                                             ", Function:" + method +
                                             ", Line#:" + lineNumber +
                                             ", Msg:", inLogMessage);
                #else

                inLogMessage = string.Concat("Module:" + gbModuleName +
                                             ", Msg:", inLogMessage);
                #endif

                // Log the given message
                if (inlevel == Level.Info)
                {
                    log.Info(inLogMessage);
                }

                if (inlevel == Level.Debug)
                {
                    log.Debug(inLogMessage);
                }

                if (inlevel == Level.Warn)
                {
                    log.Warn(inLogMessage);
                }

                if (inlevel == Level.Error)
                {
                    log.Error(inLogMessage, inException);
                }
            }
            catch (Exception ex)
            {
                //TODO: <Throw customised exception>
            }
        }

        #endregion
    }

    /// <summary>
    /// Provides freedom to customise the xmloutput of the log4net.
    /// </summary>
    public class GBXmlLayout : XmlLayoutBase
    {
        #region Protected Members

        protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
        {
            bool isException = false;

            try
            {
                // Check if the logging is done for an exception message
                if (loggingEvent.GetExceptionString().Trim() != string.Empty)
                {
                    isException = true;
                }

                //writer.WriteStartElement("LogEntry" + " timestamp=\"" + loggingEvent.TimeStamp.ToString() + "\"");
                writer.WriteStartElement("LogEntry");
                writer.WriteStartElement("Message");
                writer.WriteString(loggingEvent.RenderedMessage);
                writer.WriteEndElement();

                if (isException)
                {
                    writer.WriteStartElement("Exception");
                    writer.WriteString(loggingEvent.GetExceptionString());
                    writer.WriteEndElement();
                }

                writer.WriteEndElement();
            }
            catch (Exception ex)
            {
                //TODO: <Throw customised exception>
            }
        }

        #endregion
    }
}
